{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "EsLRm7bTDRz3"
   },
   "source": [
    "### Evaluating Tautomer Energies with Auto3D\n",
    "\n",
    "In this notebook we will generate tautomers for a molecule and use the Neural Network Potential (NNP) in Auto3D to evaluate their enegies.  This notebook borrows heavily from one of the Auto3D [demo notebooks](https://github.com/isayevlab/Auto3D_pkg/blob/main/example/tautomer.ipynb).  Please follow the links below for more information on Auto3D. \n",
    "\n",
    "- [Paper](https://pubs.acs.org/doi/10.1021/acs.jcim.2c00817)\n",
    "- [Documentation](https://auto3d.readthedocs.io/en/latest/index.html)\n",
    "- [Code](https://github.com/isayevlab/Auto3D_pkg)\n",
    "\n",
    "This workflow frequently comes into play in virtual screening.  When performing a virtual screen (VS), we typically begin by using a fast, empirical method to generate tautomers for a molecule.  While these empirical methods are fast, they sometimes generate non-physical tautomers.  As a final VS step, it's often useful to evaluate tautomer energetics and ensure that the docked tautomer is among those with the lowest energies.\n",
    "\n",
    "In the past, one had to run a computationally expensive quantum chemical calculation to obtain relative energies for tautomers.  These calculations could easily require more than a day to calculate the tautomer distribution for a single molecule.  More recently, several groups have developed neural network potentials that use data from more computationally expensive calculations to learn a quantum chemical potential.  Instead of requiring days of computation, these methods can usually calculate tautomer distributions for drug-like molecules in around a minute.  While this isn't necessarily fast enough to process billions of molecules, it can be used to filter several thousand.  By comparing the docked tautomer with the energies calculated by Auto3D we can eliminate any structures which are not in low energy tautomeric states. It's important to remember that Auto3D is trained on gas phase calculations.  The calculated values do not include a solvation component. \n",
    "\n",
    "## Important Note\n",
    "To run this notebook, you need to first set the Google Colab instance type to **GPU**.  To do this:   \n",
    "1. Select **Runtime** from the menu above\n",
    "2. Select **Change runtime type**\n",
    "3. Set **Hardware accelerator** to **T4 GPU**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Wbrjc2ZjFBn-"
   },
   "source": [
    "Install the necessary Python libraries"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "s97VSZ2c_uhA",
    "outputId": "f6f5c626-8f65-4383-f365-520a328b9987",
    "ExecuteTime": {
     "end_time": "2025-05-05T22:13:19.063062Z",
     "start_time": "2025-05-05T22:13:19.061246Z"
    }
   },
   "source": [
    "import sys\n",
    "IN_COLAB = 'google.colab' in sys.modules\n",
    "if IN_COLAB:\n",
    "    !pip install auto3D torchani rdkit mols2grid"
   ],
   "outputs": [],
   "execution_count": 1
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YBqgpuu9FPD9"
   },
   "source": [
    "Import the libraries we'll be using"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "uMphw9kPBNlU",
    "outputId": "5d989e88-e315-4134-b57c-8a418d48e9e2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.2.11\n"
     ]
    }
   ],
   "source": [
    "from rdkit import Chem\n",
    "import Auto3D\n",
    "from Auto3D.auto3D import options\n",
    "from Auto3D.tautomer import get_stable_tautomers\n",
    "from rdkit.Chem import PandasTools\n",
    "import mols2grid\n",
    "print(Auto3D.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "n7VxlgBiFaQ-"
   },
   "source": [
    "Let's start with an example molecule, this one was inspired by [1bmk](https://www.rcsb.org/structure/1BMK) in the pdb."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 167
    },
    "id": "MX0h5KsNBSl_",
    "outputId": "34e3dea8-1c29-47f2-d8f5-746892b8c67e"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<rdkit.Chem.rdchem.Mol at 0x7d553ba58cf0>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "smi_1bmk = \"Nc1nccc(n1)-c1[nH]cnc1-c1ccc(F)cc1\"\n",
    "mol_1bmk = Chem.MolFromSmiles(smi_1bmk)\n",
    "mol_1bmk"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "sQ5yND5sF-vh"
   },
   "source": [
    "Write the SMILES to disk for Auto3D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "ffIYioekBl9j"
   },
   "outputs": [],
   "source": [
    "input_path = \"1bmk_ligand.smi\"\n",
    "with open(input_path,\"w\") as ofs:\n",
    "    ofs.write(\" \".join([smi_1bmk,\"1bmk\"]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dG0EzpO4GKef"
   },
   "source": [
    "Run Auto3D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "6MuHA83IB0A3",
    "outputId": "4d04029d-53ff-455d-ccbd-41f0c39fdf0a"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:\n",
      "         _              _             _____   ____  \n",
      "        / \\     _   _  | |_    ___   |___ /  |  _ \\ \n",
      "       / _ \\   | | | | | __|  / _ \\    |_ \\  | | | |\n",
      "      / ___ \\  | |_| | | |_  | (_) |  ___) | | |_| |\n",
      "     /_/   \\_\\  \\__,_|  \\__|  \\___/  |____/  |____/  2.2.11\n",
      "              // Generating low-energy 3D structures                                      \n",
      "    \n",
      "INFO:auto3d:================================================================================\n",
      "INFO:auto3d:                               INPUT PARAMETERS\n",
      "INFO:auto3d:================================================================================\n",
      "INFO:auto3d:path: 1bmk_ligand.smi\n",
      "INFO:auto3d:k: 1\n",
      "INFO:auto3d:window: False\n",
      "INFO:auto3d:verbose: False\n",
      "INFO:auto3d:job_name: 20240511-190133-558053\n",
      "INFO:auto3d:enumerate_tautomer: True\n",
      "INFO:auto3d:tauto_engine: rdkit\n",
      "INFO:auto3d:pKaNorm: True\n",
      "INFO:auto3d:isomer_engine: rdkit\n",
      "INFO:auto3d:enumerate_isomer: True\n",
      "INFO:auto3d:mode_oe: classic\n",
      "INFO:auto3d:mpi_np: 4\n",
      "INFO:auto3d:max_confs: 10\n",
      "INFO:auto3d:use_gpu: True\n",
      "INFO:auto3d:capacity: 42\n",
      "INFO:auto3d:gpu_idx: 0\n",
      "INFO:auto3d:optimizing_engine: ANI2xt\n",
      "INFO:auto3d:patience: 200\n",
      "INFO:auto3d:opt_steps: 5000\n",
      "INFO:auto3d:convergence_threshold: 0.003\n",
      "INFO:auto3d:threshold: 0.3\n",
      "INFO:auto3d:memory: None\n",
      "INFO:auto3d:batchsize_atoms: 1024\n",
      "INFO:auto3d:input_format: smi\n",
      "INFO:auto3d:================================================================================\n",
      "INFO:auto3d:                               RUNNING PROCESS\n",
      "INFO:auto3d:================================================================================\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Checking input file...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:Checking input file...\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tThere are 1 SMILES in the input file 1bmk_ligand.smi. \n",
      "\tAll SMILES and IDs are valid.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:\tThere are 1 SMILES in the input file 1bmk_ligand.smi. \n",
      "\tAll SMILES and IDs are valid.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Suggestions for choosing isomer_engine and optimizing_engine: \n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:Suggestions for choosing isomer_engine and optimizing_engine: \n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\tIsomer engine options: RDKit and Omega.\n",
      "\tOptimizing engine options: ANI2x, ANI2xt and AIMNET.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:\tIsomer engine options: RDKit and Omega.\n",
      "INFO:auto3d:\tOptimizing engine options: ANI2x, ANI2xt and AIMNET.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The available memory is 15 GB.\n",
      "The task will be divided into 1 jobs.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:The available memory is 15 GB.\n",
      "INFO:auto3d:The task will be divided into 1 jobs.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Job1, number of inputs: 1\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:Job1, number of inputs: 1\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Energy unit: Hartree if implicit.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:Energy unit: Hartree if implicit.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Program running time: 1 minute(s)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:Program running time: 1 minute(s)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Output path: /content/1bmk_ligand_20240511-190133-558053/1bmk_ligand_out.sdf\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:auto3d:Output path: /content/1bmk_ligand_20240511-190133-558053/1bmk_ligand_out.sdf\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Begin to select stable tautomers based on their conformer energies...\n",
      "Done.\n",
      "The stable tautomers are stored in: /content/1bmk_ligand_20240511-190133-558053/1bmk_ligand_out_top_tautomers.sdf\n"
     ]
    }
   ],
   "source": [
    "args = options(input_path, k=1, enumerate_tautomer=True, tauto_engine=\"rdkit\",\n",
    "                optimizing_engine=\"ANI2xt\",  #ANI2xt is NNP designed for tautomers\n",
    "                max_confs=10, patience=200, use_gpu=True)\n",
    "tautomer_out = get_stable_tautomers(args, tauto_k=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "7dQZNMygGSc9"
   },
   "source": [
    "Read the output from Auto3D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "kEVLpwd8CCc_"
   },
   "outputs": [],
   "source": [
    "df = PandasTools.LoadSDF(tautomer_out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "QJcJTGS8G72M"
   },
   "source": [
    "View the Auto3D results.  The numbers below the structures are the relative tautomer energies in kcal/mol. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 371,
     "referenced_widgets": [
      "8d88dc5616db487ca1d0f224f10e9d7a",
      "bdb4e61bfe3b403f9a1d8347bb283603"
     ]
    },
    "id": "RZWd8GNYCUKn",
    "outputId": "381471ca-5e36-44b1-ef90-c0b020558517"
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "8d88dc5616db487ca1d0f224f10e9d7a",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "MolGridWidget()"
      ]
     },
     "metadata": {
      "application/vnd.jupyter.widget-view+json": {
       "colab": {
        "custom_widget_manager": {
         "url": "https://ssl.gstatic.com/colaboratory-static/widgets/colab-cdn-widget-manager/2b70e893a8ba7c0f/manager.min.js"
        }
       }
      }
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<style>\n",
       "    /* Some CSS to integrate with Jupyter more cleanly */\n",
       "    div.output_subarea {\n",
       "        /* Undo an unfortunate max-width parameter\n",
       "        that causes the output area to be too narrow\n",
       "        on smaller screens. */\n",
       "        max-width: none;\n",
       "\n",
       "        /* Align the table with the content */\n",
       "        padding: 0;\n",
       "\n",
       "        /* Let it breathe */\n",
       "        margin-top: 20px;\n",
       "    }\n",
       "</style>\n",
       "\n",
       "<iframe class=\"mols2grid-iframe\" frameborder=\"0\" width=\"100%\"\n",
       "    \n",
       "    \n",
       "    allow=\"clipboard-write\"\n",
       "    \n",
       "    \n",
       "    sandbox=\"allow-scripts allow-same-origin allow-downloads allow-popups allow-modals\"\n",
       "    \n",
       "    srcdoc=\"\n",
       "\n",
       "\n",
       "\n",
       "&lt;html lang=&quot;en&quot;&gt;\n",
       "    &lt;head&gt;\n",
       "        &lt;meta charset=&quot;UTF-8&quot; /&gt;\n",
       "        &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;\n",
       "        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;\n",
       "        &lt;title&gt;Document!&lt;/title&gt;\n",
       "\n",
       "\n",
       "\n",
       "        &lt;style&gt;\n",
       "            /**\n",
       " * General styling\n",
       " */\n",
       "body {\n",
       "    font-family: &#x27;DejaVu&#x27;, sans-serif;\n",
       "}\n",
       "h1,h2,h3,h4 {\n",
       "    margin: 0 0 10px 0;\n",
       "}\n",
       "h1 {\n",
       "    font-size: 26px;\n",
       "}\n",
       "h2 {\n",
       "    font-size: 20px;\n",
       "    font-weight: 400;\n",
       "}\n",
       "h3 {\n",
       "\tfont-size: 16px;\n",
       "}\n",
       "p {\n",
       "    margin: 0 0 10px 0;\n",
       "}\n",
       "\n",
       "\n",
       "/* Remove body margin inside iframe */\n",
       "body.m2g-inside-iframe {\n",
       "    margin: 0;\n",
       "}\n",
       "\n",
       "/* In-cell text */\n",
       "#mols2grid .data:not(.data-img) {\n",
       "    height: 16px;\n",
       "    line-height: 16px;\n",
       "}\n",
       "/* Text truncation */\n",
       "#mols2grid .data {\n",
       "    /* Break text into multiple lines (default for static)... */\n",
       "    word-wrap: normal;\n",
       "\n",
       "    /* ...or truncate it (default for interactive). */\n",
       "    overflow: hidden;\n",
       "    white-space: nowrap;\n",
       "    text-overflow: ellipsis;\n",
       "}\n",
       "\n",
       "\n",
       "/**\n",
       " * Popover\n",
       " * - - -\n",
       " * Note: this is a bootstrap variable which is not namespaced.\n",
       " * To avoid any contamination, we only style it when the\n",
       " * x-placement parameter is set.\n",
       " */\n",
       ".popover[x-placement] {\n",
       "    font-family: &#x27;DejaVu&#x27;, sans-serif;\n",
       "    background: white;\n",
       "    border: solid 1px rgba(0,0,0,.2);\n",
       "    font-size: 12px;\n",
       "    padding: 10px;\n",
       "    border-radius: 5px;\n",
       "    box-shadow: 0 0 20px rgba(0,0,0,.15);\n",
       "    user-select: none;\n",
       "}\n",
       ".popover[x-placement] h3 {\n",
       "    margin: 0;\n",
       "}\n",
       ".popover[x-placement] .arrow {\n",
       "    width: 10px;\n",
       "    height: 10px;\n",
       "    background: #fff;\n",
       "    border: solid 1px rgba(0,0,0,.2);\n",
       "    box-sizing: border-box;\n",
       "    position: absolute;\n",
       "    transform-origin: 5px 5px;\n",
       "    clip-path: polygon(0 0, 100% 0, 100% 100%);\n",
       "}\n",
       ".popover[x-placement=&#x27;left&#x27;] .arrow {\n",
       "    transform: rotate(45deg);\n",
       "    top: 50%;\n",
       "    right: -5px;\n",
       "}\n",
       ".popover[x-placement=&#x27;right&#x27;] .arrow {\n",
       "    transform: rotate(-135deg);\n",
       "    top: 50%;\n",
       "    left: -5px;\n",
       "}\n",
       ".popover[x-placement=&#x27;top&#x27;] .arrow {\n",
       "    transform: rotate(135deg);\n",
       "    left: 50%;\n",
       "    bottom: -5px;\n",
       "}\n",
       ".popover[x-placement=&#x27;bottom&#x27;] .arrow {\n",
       "    transform: rotate(-45deg);\n",
       "    left: 50%;\n",
       "    top: -5px;\n",
       "}\n",
       "            body {\n",
       "    /* Colors */\n",
       "    --m2g-black: rgba(0,0,0,.75);\n",
       "    --m2g-black-soft: rgba(0,0,0,.35);\n",
       "    --m2g-black-10: rgba(0,0,0,.1);\n",
       "    --m2g-bg: #f6f6f6;\n",
       "    --m2g-border: solid 1px rgba(0,0,0,0.2);\n",
       "    --m2g-hl: #555; /* Highlight color */\n",
       "    --m2g-hl-shadow: inset 0 0 0 1px var(--m2g-hl); /* Inset 1px shadow to make border thicker */\n",
       "    --m2g-blue: #0f62fe;\n",
       "    --m2g-blue-soft: rgba(15,98,254,.2);\n",
       "\n",
       "    /* Icons */\n",
       "    --m2g-icn-triangle: url(&#x27;data:image/svg+xml;utf8,&lt;svg width=&quot;20&quot; fill=&quot;rgba(0,0,0,.75)&quot; height=&quot;20&quot; viewBox=&quot;0 0 20 20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;path d=&quot;M9.5713 13.285L6.2543 7.757C6.0543 7.424 6.2953 7 6.6823 7L13.3173 7C13.7053 7 13.9463 7.424 13.7453 7.757L10.4283 13.285C10.2343 13.609 9.7653 13.609 9.5713 13.285Z&quot;/&gt;&lt;/svg&gt;&#x27;);\n",
       "    --m2g-icn-triangle-white: url(&#x27;data:image/svg+xml;utf8,&lt;svg width=&quot;20&quot; fill=&quot;white&quot; height=&quot;20&quot; viewBox=&quot;0 0 20 20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;path d=&quot;M9.5713 13.285L6.2543 7.757C6.0543 7.424 6.2953 7 6.6823 7L13.3173 7C13.7053 7 13.9463 7.424 13.7453 7.757L10.4283 13.285C10.2343 13.609 9.7653 13.609 9.5713 13.285Z&quot;/&gt;&lt;/svg&gt;&#x27;);\n",
       "    --m2g-icn-cb-white: url(&#x27;data:image/svg+xml;utf8,&lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; fill=&quot;none&quot; stroke=&quot;white&quot; stroke-width=&quot;2.5&quot; stroke-linecap=&quot;round&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;path d=&quot;M4 7.65686L7 10.6569L12.6569 5.00001&quot;/&gt;&lt;/svg&gt;&#x27;);\n",
       "    \n",
       "    /* Border radius */\n",
       "    --m2g-br: 3px;\n",
       "    --m2g-br-l: var(--m2g-br) 0 0 var(--m2g-br); /* Left-only */\n",
       "    --m2g-br-r: 0 var(--m2g-br) var(--m2g-br) 0; /* Right-only */\n",
       "\n",
       "    /* Text */\n",
       "    --m2g-fs: 14px; /* UI font-size */\n",
       "    --m2g-fs-cell: 12px; /* Cell font-size */\n",
       "\n",
       "    /* Transition speeds */\n",
       "    --m2g-trans: 150ms;\n",
       "\n",
       "    /* Layout */\n",
       "    --m2g-h: 40px; /* Form element height */\n",
       "}\n",
       "\n",
       "/* Styling */\n",
       "#mols2grid {\n",
       "    font-family: &#x27;DejaVu&#x27;, sans-serif;\n",
       "    font-size: var(--m2g-fs);\n",
       "}\n",
       "\n",
       "/* Fixes */\n",
       "#mols2grid *,\n",
       "#mols2grid *::before,\n",
       "#mols2grid *::after {\n",
       "    box-sizing: border-box;\n",
       "    outline: none;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Functions section\n",
       " */\n",
       "\n",
       "#mols2grid .m2g-functions {\n",
       "    display: flex;\n",
       "}\n",
       "#mols2grid .m2g-functions .m2g-row {\n",
       "    flex: 0;\n",
       "    display: flex;\n",
       "}\n",
       "\n",
       "/* Individual elements don&#x27;t scale */\n",
       "#mols2grid .m2g-functions .m2g-row &gt; * {\n",
       "    flex: 0 0;\n",
       "    margin-right: 10px;\n",
       "}\n",
       "#mols2grid .m2g-functions .m2g-row:last-child &gt; *:last-child {\n",
       "    margin-right: 0;\n",
       "}\n",
       "\n",
       "/* Row 1: pagination + gap + sort */\n",
       "#mols2grid .m2g-functions .m2g-row:first-child {\n",
       "    flex: 1; /* Scale */\n",
       "}\n",
       "#mols2grid .m2g-functions .m2g-gap {\n",
       "    /* The gap in between will scale, so the pagination\n",
       "    stays on the left, while the rest moves to the right */\n",
       "    flex: 1;\n",
       "    margin-right: 0;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/*\n",
       " * Pagination\n",
       " */\n",
       "\n",
       "#mols2grid ul.m2g-pagination {\n",
       "    /* Unset defaults */\n",
       "    list-style-type: none;\n",
       "    margin-block-start: 0;\n",
       "    margin-block-end: 0;\n",
       "    margin-inline-start: 0;\n",
       "    margin-inline-end: 0;\n",
       "    padding-inline-start: 0;\n",
       "\n",
       "    /* Custom */\n",
       "    display: flex;\n",
       "}\n",
       "#mols2grid ul.m2g-pagination li {\n",
       "    background: var(--m2g-bg) ;\n",
       "    border: var(--m2g-border);\n",
       "    height: var(--m2g-h);\n",
       "    min-width: calc(var(--m2g-h) + 1px);\n",
       "    position: relative;\n",
       "    user-select: none;\n",
       "    \n",
       "    /* Compensate for double border */\n",
       "    margin-right: -1px;\n",
       "    \n",
       "    /* Center text */\n",
       "    display: flex;\n",
       "    align-items: center;\n",
       "    justify-content: center;\n",
       "}\n",
       "#mols2grid ul.m2g-pagination li:last-child {\n",
       "    min-width: var(--m2g-h);\n",
       "}\n",
       "#mols2grid ul.m2g-pagination li a {\n",
       "    text-decoration: none;\n",
       "    color: var(--m2g-black);\n",
       "    padding: 0 10px;\n",
       "    width: 100%;\n",
       "    height: var(--m2g-h);\n",
       "    line-height: var(--m2g-h);\n",
       "    text-align: center;\n",
       "    /* Compensate for border so there&#x27;s no gap between click areas  */\n",
       "    margin: 0 -1px;\n",
       "}\n",
       "\n",
       "/* Corner shape */\n",
       "#mols2grid ul.m2g-pagination li:first-child {\n",
       "    border-radius: var(--m2g-br-l);\n",
       "}\n",
       "#mols2grid ul.m2g-pagination li:last-child {\n",
       "    border-radius: var(--m2g-br-r);\n",
       "    margin-right: 0;\n",
       "}\n",
       "\n",
       "/* Focus state */\n",
       "#mols2grid ul.m2g-pagination li:focus-within {\n",
       "    border-color: var(--m2g-hl);\n",
       "    box-shadow: var(--m2g-hl-shadow);\n",
       "    z-index: 1;\n",
       "}\n",
       "\n",
       "/* Active state */\n",
       "#mols2grid ul.m2g-pagination li.active {\n",
       "    background: var(--m2g-hl);\n",
       "    z-index: 1;\n",
       "}\n",
       "#mols2grid ul.m2g-pagination li.active a {\n",
       "    cursor: default;\n",
       "    color: #fff;\n",
       "}\n",
       "\n",
       "/* Disabled sate */\n",
       "#mols2grid ul.m2g-pagination li.disabled a {\n",
       "    cursor: default;\n",
       "    color: rgba(0,0,0,.25);\n",
       "    pointer-events: none;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/*\n",
       " * Dropdowns\n",
       " */\n",
       "\n",
       "#mols2grid ::placeholder {\n",
       "    color: var(--m2g-black-soft);\n",
       "}\n",
       "#mols2grid .m2g-dropdown {\n",
       "    height: var(--m2g-h);\n",
       "    background: var(--m2g-bg);\n",
       "    border: var(--m2g-border);\n",
       "    border-radius: var(--m2g-br);\n",
       "    position: relative;\n",
       "}\n",
       "#mols2grid .m2g-dropdown select {\n",
       "    -webkit-appearance: none;\n",
       "    -moz-appearance: none;\n",
       "    -ms-appearance: none;\n",
       "    appearance: none;\n",
       "    background: transparent;\n",
       "    border: none;\n",
       "    height: 100%;\n",
       "    padding: 0 13px;\n",
       "    min-width: 0;\n",
       "    max-width: 250px;\n",
       "    color: var(--m2g-black);\n",
       "    cursor: pointer;\n",
       "}\n",
       "\n",
       "/* Icon */\n",
       "#mols2grid .m2g-dropdown .m2g-icon {\n",
       "    width: 30px;\n",
       "    height: var(--m2g-h);\n",
       "    display: flex;\n",
       "    align-items: center;\n",
       "    justify-content: center;\n",
       "    position: absolute;\n",
       "    top: 0;\n",
       "    right: 0;\n",
       "    pointer-events: none;\n",
       "}\n",
       "#mols2grid .m2g-dropdown .m2g-icon svg:not(.m2g-stroke) {\n",
       "    fill: var(--m2g-black);\n",
       "}\n",
       "#mols2grid .m2g-dropdown .m2g-icon svg.m2g-stroke {\n",
       "    stroke: var(--m2g-black);\n",
       "}\n",
       "\n",
       "/* Display */\n",
       "/* We hide the native select element because\n",
       " * it is limited in styling. Instead, we display\n",
       " * the selected value in a div. */\n",
       "#mols2grid .m2g-dropdown .m2g-display {\n",
       "    position: absolute;\n",
       "    left: 0;\n",
       "    right: 0;\n",
       "    top: 0;\n",
       "    bottom: 0;\n",
       "    pointer-events: none;\n",
       "    color: var(--m2g-black);\n",
       "    line-height: var(--m2g-h);\n",
       "    padding: 0 25px 0 13px;\n",
       "\n",
       "    /* Truncate dropdown text */\n",
       "    white-space: nowrap;\n",
       "\ttext-overflow: ellipsis;\n",
       "\toverflow: hidden;\n",
       "}\n",
       "\n",
       "/* Focus state */\n",
       "#mols2grid .m2g-dropdown:focus-within {\n",
       "    border-color: var(--m2g-hl);\n",
       "    box-shadow: var(--m2g-hl-shadow);\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Action dropdown\n",
       " */\n",
       "\n",
       "#mols2grid .m2g-dropdown.m2g-actions {\n",
       "    width: var(--m2g-h);\n",
       "    padding: 0;\n",
       "}\n",
       "#mols2grid .m2g-dropdown.m2g-actions select {\n",
       "    opacity: 0;\n",
       "    width: var(--m2g-h);\n",
       "}\n",
       "#mols2grid .m2g-dropdown.m2g-actions .m2g-icon {\n",
       "    width: var(--m2g-h);\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/*\n",
       " * Sort dropdown\n",
       " */\n",
       "\n",
       "#mols2grid .m2g-dropdown.m2g-sort {\n",
       "    flex: 0 0 200px;\n",
       "    width: 200px; /* Needed in addition to flex-basis for small sizes! */\n",
       "    border-radius: var(--m2g-br);\n",
       "    background: var(--m2g-bg);\n",
       "    display: flex;\n",
       "}\n",
       "\n",
       "/* Dropdown */\n",
       "#mols2grid .m2g-dropdown.m2g-sort select {\n",
       "    flex: 1 1;\n",
       "    opacity: 0;\n",
       "    /* padding-right: 70px; Space for &quot;Sort:&quot; */\n",
       "    box-sizing: border-box;\n",
       "}\n",
       "\n",
       "/* Sort order */\n",
       "#mols2grid .m2g-dropdown.m2g-sort .m2g-order {\n",
       "    background: var(--m2g-bg) var(--m2g-icn-triangle) no-repeat center;\n",
       "    flex: 0 0 30px;\n",
       "    height: 100%;\n",
       "    border-left: var(--m2g-border);\n",
       "    cursor: pointer;\n",
       "}\n",
       "#mols2grid .m2g-dropdown.m2g-sort.m2d-arrow-desc .m2g-order {\n",
       "    transform: rotate(180deg);\n",
       "    border-left: none;\n",
       "    border-right: var(--m2g-border);\n",
       "}\n",
       "\n",
       "/* Display */\n",
       "#mols2grid .m2g-dropdown.m2g-sort .m2g-display {\n",
       "    right: 31px;\n",
       "    padding-right: 13px;\n",
       "}\n",
       "#mols2grid .m2g-dropdown.m2g-sort .m2g-display::before {\n",
       "    content: &#x27;Sort: &#x27;;\n",
       "}\n",
       "\n",
       "/* Focus state */\n",
       "#mols2grid .m2g-dropdown.m2g-sort:focus-within .m2g-display,\n",
       "#mols2grid .m2g-dropdown.m2g-sort:focus-within .m2g-order {\n",
       "    background-color: transparent;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/*\n",
       " * Search bar\n",
       " */\n",
       "\n",
       "#mols2grid .m2g-search-wrap {\n",
       "    height: var(--m2g-h);\n",
       "    display: flex;\n",
       "    align-items: center;\n",
       "    justify-content: flex-end;\n",
       "    background: var(--m2g-bg);\n",
       "    border: var(--m2g-border);\n",
       "    border-radius: var(--m2g-br);\n",
       "}\n",
       "#mols2grid .m2g-searchbar {\n",
       "    width: 170px;\n",
       "    height: var(--m2g-h);\n",
       "    padding: 0 13px;\n",
       "    border: none;\n",
       "    color: var(--m2g-black);\n",
       "    cursor: text;\n",
       "    background: transparent;\n",
       "}\n",
       "\n",
       "/* Focus state */\n",
       "#mols2grid .m2g-search-wrap:focus-within {\n",
       "    border-color: var(--m2g-hl);\n",
       "    box-shadow: var(--m2g-hl-shadow);\n",
       "}\n",
       "\n",
       "/* Option buttons */\n",
       "#mols2grid .m2g-search-options {\n",
       "    font-size: 12px;\n",
       "    display: flex;\n",
       "    height: calc(1.5em + .75rem);\n",
       "    line-height: calc(1.5em + .75rem);\n",
       "    margin-right: 5px;\n",
       "    border-radius: var(--m2g-br);\n",
       "    color: var(--m2g-black);\n",
       "}\n",
       "#mols2grid .m2g-search-options .m2g-option {\n",
       "    background: var(--m2g-black-10);\n",
       "    padding: 0 13px;\n",
       "    cursor: default;\n",
       "    user-select: none;\n",
       "}\n",
       "#mols2grid .m2g-search-options .m2g-option:not(.sel) {\n",
       "    cursor: pointer;\n",
       "}\n",
       "#mols2grid .m2g-search-options .m2g-option:first-child {\n",
       "    border-radius: 2px 0 0 2px;\n",
       "}\n",
       "#mols2grid .m2g-search-options .m2g-option:last-child {\n",
       "    border-radius: 0 2px 2px 0;\n",
       "}\n",
       "#mols2grid .m2g-search-options .m2g-option.sel {\n",
       "    background: var(--m2g-hl);\n",
       "    color: #fff;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Grid\n",
       " */\n",
       "\n",
       "/* Container */\n",
       "#mols2grid .m2g-list {\n",
       "    display: flex;\n",
       "    flex-wrap: wrap;\n",
       "    align-items: flex-start;\n",
       "    justify-content: flex-start;\n",
       "    padding: 1px; /* Compensate for negative padding on cell */\n",
       "    user-select: none;\n",
       "    margin: 0px;\n",
       "    margin-top: 20px;\n",
       "    font-family: &#x27;DejaVu&#x27;, sans-serif;\n",
       "    \n",
       "}\n",
       "\n",
       "/* Cell */\n",
       "#mols2grid .m2g-cell {\n",
       "    border: 1px solid #cccccc;\n",
       "    text-align: center;\n",
       "    vertical-align: top;\n",
       "    font-family: var(--font-family);\n",
       "    padding: 10px;\n",
       "    padding-top: max(10px, 20px);\n",
       "    margin: -1px -1px 0 0;\n",
       "    flex: 1 0 200px;\n",
       "    position: relative;\n",
       "    font-size: var(--m2g-fs-cell);\n",
       "    cursor: pointer;\n",
       "    color: var(--m2g-black);\n",
       "    overflow: hidden;\n",
       "    box-sizing: border-box;\n",
       "    background-color: white;\n",
       "}\n",
       "#mols2grid .m2g-cell:focus {\n",
       "    z-index: 1;\n",
       "    border-color: var(--m2g-hl);\n",
       "    box-shadow: var(--m2g-hl-shadow);\n",
       "}\n",
       "\n",
       "/* Phantom cells to maintain grid structure with less results */\n",
       "#mols2grid .m2g-cell.m2g-phantom {\n",
       "    border: none;\n",
       "    pointer-events: none;\n",
       "    height: 0;\n",
       "    padding: 0;\n",
       "}\n",
       "\n",
       "/* Checkbox &amp; ID */\n",
       "#mols2grid .m2g-cb-wrap {\n",
       "    position: absolute;\n",
       "    top: 3px;\n",
       "    left: 3px;\n",
       "    display: flex;\n",
       "    border-radius: 2px;\n",
       "    font-size: 0;\n",
       "    line-height: 0;\n",
       "    padding: 3px;\n",
       "    padding-right: 0;\n",
       "}\n",
       "#mols2grid .m2g-cb-wrap input[type=checkbox] {\n",
       "    display: none;\n",
       "}\n",
       "#mols2grid .m2g-cb-wrap input[type=checkbox] + .m2g-cb {\n",
       "\twidth: 16px;\n",
       "\theight: 16px;\n",
       "\tbox-sizing: border-box;\n",
       "\tbackground: #fff;\n",
       "\tborder: var(--m2g-border);\n",
       "\tborder-radius: 2px;\n",
       "    margin-right: 5px;\n",
       "}\n",
       "#mols2grid .m2g-cb-wrap input[type=checkbox]:checked + .m2g-cb {\n",
       "    border: none;\n",
       "    background-color: var(--m2g-blue);\n",
       "    background-image: var(--m2g-icn-cb-white);\n",
       "}\n",
       "#mols2grid .m2g-tooltip {\n",
       "    /* This is a div spanning full cell size where the\n",
       "    tooltip is rendered around, because you can&#x27;t attach\n",
       "    it to the parent due to list.js limitation. */\n",
       "    width: 100%;\n",
       "    height: 100%;\n",
       "    position: absolute;\n",
       "    left: 0;\n",
       "    top: 0;\n",
       "    z-index: -1;\n",
       "    pointer-events: none;\n",
       "    opacity: 0;\n",
       "}\n",
       "#mols2grid .m2g-cell:has(:checked) {\n",
       "    background: #ffd !important; /* Overrides user-set background color */\n",
       "}\n",
       "#mols2grid .data-mols2grid-id-display {\n",
       "    font-size: var(--m2g-fs-cell);\n",
       "    line-height: 16px;\n",
       "}\n",
       "#mols2grid .m2g-cb-wrap input[type=checkbox] + .data-mols2grid-id-display {\n",
       "    padding: 0 5px 0 5px;\n",
       "}\n",
       "#mols2grid .m2g-cb-wrap .data-name-display {\n",
       "    font-size: var(--m2g-fs);\n",
       "    line-height: 16px;\n",
       "}\n",
       "\n",
       "/* Info + callback button wrap (28px high) */\n",
       "#mols2grid .m2g-cell-actions {\n",
       "    position: absolute;\n",
       "    top: 0;\n",
       "    right: 0;\n",
       "    display: flex;\n",
       "    flex-direction: row;\n",
       "    font-size: 0;\n",
       "    line-height: 0;\n",
       "    \n",
       "    /* background: yellow; */\n",
       "}\n",
       "\n",
       "/* Info button */\n",
       "#mols2grid .m2g-info {\n",
       "    width: 28px;\n",
       "    height: 28px;\n",
       "    border-radius: 2px;\n",
       "    line-height: 28px;\n",
       "    font-size: min(14px, 12px);\n",
       "    font-family: Georgia, serif;\n",
       "    font-style: italic;\n",
       "    padding: 0;\n",
       "    text-align: center;\n",
       "}\n",
       "#mols2grid .m2g-keep-tooltip .m2g-info {\n",
       "    color: #fff;\n",
       "}\n",
       "#mols2grid .m2g-keep-tooltip .m2g-info::before {\n",
       "    content: &#x27;i&#x27;;\n",
       "    width: 18px;\n",
       "    height: 18px;\n",
       "    line-height: 18px;\n",
       "    background: var(--m2g-hl);\n",
       "    position: absolute;\n",
       "    left: 5px;\n",
       "    top: 5px;\n",
       "    border-radius: 9px;\n",
       "}\n",
       "\n",
       "/* Callback button */\n",
       "#mols2grid .m2g-callback {\n",
       "    width: 28px;\n",
       "    height: 28px;\n",
       "    cursor: pointer;\n",
       "}\n",
       "#mols2grid .m2g-callback::after {\n",
       "    content: &#x27;&#x27;;\n",
       "    display: block;\n",
       "    width: 16px;\n",
       "    height: 16px;\n",
       "    margin: 6px;\n",
       "    border: var(--m2g-border);\n",
       "    border-radius: 2px;\n",
       "    background: var(--m2g-bg) var(--m2g-icn-triangle) no-repeat center;\n",
       "    transform: rotate(-90deg);\n",
       "}\n",
       "\n",
       "/* Image */\n",
       "#mols2grid .m2g-cell .data-img {\n",
       "    padding: 0;\n",
       "}\n",
       "#mols2grid .m2g-cell img,\n",
       "#mols2grid .m2g-cell svg {\n",
       "    max-width: 100%;\n",
       "    height: auto;\n",
       "    padding: 0;\n",
       "}\n",
       "#mols2grid .m2g-cell svg &gt; rect:first-child {\n",
       "    /* Remove the SVG background */\n",
       "    fill: transparent !important;\n",
       "}\n",
       "\n",
       "/* Text below image */\n",
       ".m2g-copy-blink {\n",
       "    animation: m2g-blink var(--m2g-trans) 3;\n",
       "}\n",
       "@keyframes m2g-blink {\n",
       "    0% {\n",
       "        opacity: 1;\n",
       "    }\n",
       "    49% {\n",
       "        opacity: 1;\n",
       "    }\n",
       "    50% {\n",
       "        opacity: 0;\n",
       "    }\n",
       "    100% {\n",
       "        opacity: 0;\n",
       "    }\n",
       "}\n",
       "\n",
       "/* Copyable text */\n",
       ".copy-me {\n",
       "    position: relative;\n",
       "    cursor: pointer;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Modal popup\n",
       " * - - -\n",
       " * Triggered by make_popup_callback()\n",
       " * See https://mols2grid.readthedocs.io/en/latest/notebooks/callbacks.html#Display-a-popup-containing-descriptors\n",
       " */\n",
       "\n",
       "/* Container */\n",
       "#m2g-modal-container {\n",
       "    display: flex;\n",
       "    align-items: center;\n",
       "    justify-content: center;\n",
       "    background: var(--m2g-black-10);\n",
       "    position: fixed;\n",
       "    top: 0;\n",
       "    left: 0;\n",
       "    z-index: 1;\n",
       "    width: 100%;\n",
       "    height: 100%;\n",
       "    \n",
       "    /* Transition */\n",
       "    opacity: 0;\n",
       "    transition: opacity var(--m2g-trans) linear;\n",
       "}\n",
       "\n",
       "/* Modal */\n",
       "#m2g-modal {\n",
       "    background: #fff;\n",
       "    border-radius: var(--m2g-br);\n",
       "    box-shadow: 0 0 30px var(--m2g-black-10);\n",
       "    padding: 20px;\n",
       "    position: relative;\n",
       "    max-width: calc(100% - 80px);\n",
       "    max-height: calc(100% - 80px);\n",
       "    display: flex;\n",
       "    flex-direction: column;\n",
       "    min-width: 26px;\n",
       "\n",
       "    /* Transition */\n",
       "    opacity: 0;\n",
       "    transform: translate(0, 5px);\n",
       "    transition: transform var(--m2g-trans) ease-in-out, opacity var(--m2g-trans) linear;\n",
       "}\n",
       "#m2g-modal .m2g-modal-header {\n",
       "    flex: 0 0 26px;\n",
       "    margin-bottom: 10px;\n",
       "}\n",
       "#m2g-modal .m2g-modal-header h2 {\n",
       "    margin-bottom: 0;\n",
       "}\n",
       "#m2g-modal .m2g-modal-header h2 + p {\n",
       "    font-size: 15px;\n",
       "}\n",
       "#m2g-modal .m2g-modal-body {\n",
       "    flex: 1;\n",
       "    position: relative;\n",
       "}\n",
       "\n",
       "/* Transition */\n",
       "#m2g-modal-container.show {\n",
       "    opacity: 1;\n",
       "}\n",
       "#m2g-modal-container.show #m2g-modal {\n",
       "    opacity: 1;\n",
       "    transform: translate(0, 0);\n",
       "}\n",
       "\n",
       "/* Header + close btn */\n",
       "#m2g-modal h2 {\n",
       "    line-height: 26px;\n",
       "    padding-right: 40px;\n",
       "    text-transform: capitalize;\n",
       "}\n",
       "#m2g-modal h3 {\n",
       "    \n",
       "}\n",
       "#m2g-modal button.close {\n",
       "    background: transparent;\n",
       "    padding: 0;\n",
       "    color: var(--m2g-black);\n",
       "    font-size: 1.5rem;\n",
       "    width: 40px;\n",
       "    height: 40px;\n",
       "    position: absolute;\n",
       "    top: 13px;\n",
       "    right: 13px;\n",
       "    border: none;\n",
       "}\n",
       "\n",
       "/* Image */\n",
       "#m2g-modal .svg-wrap svg {\n",
       "    max-width: 100%;\n",
       "    margin-bottom: 20px;\n",
       "}\n",
       "\n",
       "/* Separator */\n",
       "hr {\n",
       "    width: 100%;\n",
       "    height: 1px;\n",
       "    background: #ddd;\n",
       "    margin: 15px 0;\n",
       "    border: none;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Hover states\n",
       " */\n",
       "@media (hover:hover) {\n",
       "    /* Pagination */\n",
       "    #mols2grid ul.m2g-pagination li:not(.active):not(.disabled):hover {\n",
       "        background: #f0f0f0;\n",
       "        z-index: 1;\n",
       "    }\n",
       "    #mols2grid ul.m2g-pagination li.active + li:hover {\n",
       "        /* Keeping the hover border consiistent */\n",
       "        margin-left: 1px;\n",
       "        border-left: none;\n",
       "        min-width: 40px;\n",
       "    }\n",
       "\n",
       "    /* Dropdowns &amp; search */\n",
       "    #mols2grid .m2g-dropdown:not(:focus-within):hover,\n",
       "    #mols2grid .m2g-search-wrap:not(:focus-within):hover,\n",
       "    #mols2grid .m2g-sort:not(:focus-within) .m2g-order:hover {\n",
       "        background-color: #f0f0f0;\n",
       "    }\n",
       "    #mols2grid .m2g-search-wrap:not(:focus-within):hover {\n",
       "        background: #fff;\n",
       "        border-color: rgba(0,0,0,.3);\n",
       "    }\n",
       "    /* Hocus pocus to have separate hover states for dropdown and arrow */\n",
       "    #mols2grid .m2g-dropdown.m2g-sort:not(:focus-within):hover .m2g-order:not(:hover) + .m2g-display {\n",
       "        background-color: transparent;\n",
       "    }\n",
       "\n",
       "    /* Search options */\n",
       "    #mols2grid .m2g-search-options .m2g-option:not(.sel):hover {\n",
       "        background: rgba(0,0,0,.15);\n",
       "    }\n",
       "\n",
       "    /* Grid */\n",
       "    /* Note: this is in an ::after pseudo element, so the transparent\n",
       "    hover color plays nice with the cell background color. */\n",
       "    #mols2grid .m2g-cell:hover::after {\n",
       "        content: &#x27;&#x27;;\n",
       "        width: 100%;\n",
       "        height: 100%;\n",
       "        position: absolute;\n",
       "        top: 0;\n",
       "        left: 0;\n",
       "        background-color: rgba(0,0,0,0.05);\n",
       "        pointer-events: none;\n",
       "    }\n",
       "\n",
       "    /* info button */\n",
       "    #mols2grid .m2g-info:hover::before {\n",
       "        content: &#x27;i&#x27;;\n",
       "        color: #fff;\n",
       "        width: 18px;\n",
       "        height: 18px;\n",
       "        line-height: 18px;\n",
       "        background: var(--m2g-hl);\n",
       "        position: absolute;\n",
       "        left: 5px;\n",
       "        top: 5px;\n",
       "        border-radius: 9px;\n",
       "    }\n",
       "    \n",
       "    /* Callback button */\n",
       "    #mols2grid .m2g-callback:hover::after {\n",
       "        background-color: var(--m2g-black);\n",
       "        background-image: var(--m2g-icn-triangle-white);\n",
       "        border-color: transparent;\n",
       "    }\n",
       "\n",
       "    /* Copyable text */\n",
       "    .copy-me:hover {\n",
       "        text-decoration: underline;\n",
       "        text-decoration-color: var(--m2g-blue);\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Responsive behavior.\n",
       " * - - -\n",
       " * Note: container queries won&#x27;t work in older browsers,\n",
       " * but this is purely aesthetical behavior so that&#x27;s ok.\n",
       " * https://caniuse.com/css-container-queries\n",
       " */\n",
       "\n",
       "/* This sets the msg-list div as reference container */\n",
       "#mols2grid {\n",
       "    container-type: inline-size;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Functions section\n",
       " */\n",
       "\n",
       "/* When there&#x27;s not enough space to put everything in one row, we break it into two.\n",
       " * - - -\n",
       " * 870px = pagination 280 + sort 200 + search 300 + menu 40 + (3*10 gap) = 850 + 20 buffer.\n",
       " * Buffer required because the button width inside the search depends on the font.\n",
       " */\n",
       "@container (max-width: 870px) {\n",
       "    #mols2grid .m2g-functions {\n",
       "        flex-direction: column-reverse;\n",
       "        gap: 10px;\n",
       "    }\n",
       "    #mols2grid .m2g-functions .m2g-row:last-child {\n",
       "        justify-content: flex-end;\n",
       "    }\n",
       "    #mols2grid .m2g-functions .m2g-row:first-child *:last-child {\n",
       "        margin-right: 0;\n",
       "    }\n",
       "}\n",
       "\n",
       "/* When there&#x27;s not enough room for pagination + sort on one row,\n",
       " * we reduce the sort drodpwon width.\n",
       " */\n",
       "@container (max-width: 500px) {\n",
       "    #mols2grid .m2g-functions .m2g-sort {\n",
       "        width: 80px;\n",
       "        flex-basis: 80px;\n",
       "    }\n",
       "    #mols2grid .m2g-functions .m2g-sort .m2g-display {\n",
       "        font-size: 0;\n",
       "        line-height: 0;\n",
       "        padding-right: 0;\n",
       "    }\n",
       "    #mols2grid .m2g-functions .m2g-sort .m2g-display::before {\n",
       "        content: &#x27;Sort&#x27;;\n",
       "        font-size: var(--m2g-fs);\n",
       "        line-height: var(--m2g-h);\n",
       "    }\n",
       "}\n",
       "\n",
       "/* When there&#x27;s not enough room for pagination + reduced sort on one row,\n",
       " * we reduce the pagination width.\n",
       " */\n",
       "@container (max-width: 500px) {\n",
       "    /* We&#x27;re overriding min-width from different\n",
       "    locations, including responsive rules */\n",
       "    #mols2grid ul.m2g-pagination li,\n",
       "    #mols2grid ul.m2g-pagination li:last-child,\n",
       "    #mols2grid ul.m2g-pagination li.active + li:hover {\n",
       "        min-width: 0;\n",
       "    }\n",
       "}\n",
       "\n",
       "/* When there&#x27;s not enough room for searchbar + menu\n",
       " * we scale down the searchbar to fit the container.\n",
       " */\n",
       "@container (max-width: 370px) {\n",
       "    #mols2grid .m2g-functions .m2g-row .m2g-search-wrap {\n",
       "        flex: 1;\n",
       "    }\n",
       "    #mols2grid .m2g-searchbar {\n",
       "        width: calc(100% - 50px);\n",
       "    }\n",
       "    #mols2grid .m2g-search-options {\n",
       "        width: 50px;\n",
       "    }\n",
       "\n",
       "    /* Collapse options in T/M buttons */\n",
       "    #mols2grid .m2g-search-options .m2g-option {\n",
       "        width: 25px;\n",
       "        text-align: center;\n",
       "        padding: 0;\n",
       "        overflow: hidden;\n",
       "    }\n",
       "    #mols2grid .m2g-search-options .m2g-option:first-child::before {\n",
       "        content: &#x27;T\\A&#x27;\n",
       "    }\n",
       "    #mols2grid .m2g-search-options .m2g-option:last-child::before {\n",
       "        content: &#x27;S\\A&#x27;\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Grid\n",
       " */\n",
       "\n",
       "/* When there&#x27;s room for 5 columns, fall back to 4 */\n",
       "@container (min-width: 799px) and (max-width: 1199px) {\n",
       "    #mols2grid .m2g-cell {\n",
       "        flex-basis: calc(100% / 4);\n",
       "    }\n",
       "}\n",
       "\n",
       "/* When there&#x27;s room for 7-11 columns, fall back to 6 */\n",
       "@container (min-width: 1199px) and (max-width: 2399px) {\n",
       "    #mols2grid .m2g-cell {\n",
       "        flex-basis: calc(100% / 6);\n",
       "    }\n",
       "}\n",
       "\n",
       "/* When there&#x27;s room for 13+ columns, fall back to 12 */\n",
       "@container (min-width: 2399px) {\n",
       "    #mols2grid .m2g-cell {\n",
       "        flex-basis: calc(100% / 12);\n",
       "    }\n",
       "}\n",
       "\n",
       "            /* Custom CSS */\n",
       "            \n",
       "        &lt;/style&gt;\n",
       "        &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/list.js/2.3.1/list.min.js&quot;&gt;&lt;/script&gt;\n",
       "&lt;script src=&quot;https://code.jquery.com/jquery-3.6.0.min.js&quot; integrity=&quot;sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;\n",
       "&lt;script src=&quot;https://unpkg.com/@rdkit/rdkit@2022.3.1/Code/MinimalLib/dist/RDKit_minimal.js&quot;&gt;&lt;/script&gt;\n",
       "        &lt;script&gt;\n",
       "    // Set iframe height to fit content.\n",
       "    function fitIframe(iframe) {\n",
       "        // Ignore when there&#x27;s no iframe\n",
       "        if (!iframe) return\n",
       "\n",
       "        // Only fit height when no specific height was given.\n",
       "        if (iframe.getAttribute(&#x27;height&#x27;)) return;\n",
       "\n",
       "        // Initial fit + refit whenever the window width changes.\n",
       "        _fit()\n",
       "        $(window).on(&#x27;resize&#x27;, function() {\n",
       "            if (window.innerWidth != window.prevInnerWidth) {\n",
       "                window.prevInnerWidth = window.innerWidth\n",
       "                _fit();\n",
       "            }\n",
       "        })\n",
       "\n",
       "        // Fit iframe height to content height.\n",
       "        function _fit() {\n",
       "            var height = iframe.contentDocument.body.scrollHeight + 18 + &#x27;px&#x27;;\n",
       "            iframe.style.height = height;\n",
       "        }\n",
       "    }\n",
       "&lt;/script&gt;\n",
       "\n",
       "&lt;!-- prettier-ignore --&gt;\n",
       "&lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js&quot; integrity=&quot;sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns&quot; crossorigin=&quot;anonymous&quot;&gt;&lt;/script&gt;\n",
       "        \n",
       "        &lt;!-- Custom header --&gt;\n",
       "        \n",
       "\n",
       "\n",
       "\n",
       "\n",
       "    &lt;/head&gt;\n",
       "    &lt;body class=&quot;m2g-inside-iframe&quot;&gt;\n",
       "\n",
       "\n",
       "\n",
       "        &lt;div id=&quot;mols2grid&quot; class=&quot;grid-default&quot;&gt;\n",
       "            &lt;!-- Pagination &amp; search --&gt;\n",
       "            &lt;div class=&quot;m2g-functions&quot;&gt;\n",
       "                \n",
       "                &lt;div class=&quot;m2g-row&quot;&gt;\n",
       "                    &lt;!-- Pagination --&gt;\n",
       "                    &lt;ul class=&quot;m2g-pagination&quot; class=&quot;d-flex&quot;&gt;&lt;/ul&gt;\n",
       "                    &lt;div class=&quot;m2g-gap&quot;&gt;&lt;/div&gt;\n",
       "\n",
       "                    &lt;!-- Sort dropdown --&gt;\n",
       "                    &lt;div class=&quot;m2g-dropdown m2g-sort&quot;&gt;\n",
       "                        &lt;select&gt;\n",
       "                            \n",
       "                            \n",
       "                                \n",
       "                                \n",
       "                                \n",
       "                            &lt;option value=&quot;mols2grid-id&quot; selected&gt;Index&lt;/option&gt;\n",
       "                                \n",
       "                            \n",
       "                                \n",
       "                                \n",
       "                                \n",
       "                            &lt;option value=&quot;data-E_tautomer_relative(kcal/mol)&quot;&gt;E_tautomer_relative(kcal/mol)&lt;/option&gt;\n",
       "                                \n",
       "                            \n",
       "                            \n",
       "                            &lt;option value=&quot;checkbox&quot;&gt;Selected&lt;/option&gt;\n",
       "                            \n",
       "                        &lt;/select&gt;\n",
       "                        &lt;div class=&quot;m2g-order&quot;&gt;&lt;/div&gt;\n",
       "                        &lt;div class=&quot;m2g-display&quot;&gt;\n",
       "                            Index\n",
       "                        &lt;/div&gt;\n",
       "                    &lt;/div&gt;\n",
       "                &lt;/div&gt;\n",
       "                &lt;div class=&quot;m2g-row&quot;&gt;\n",
       "                    &lt;!-- Search bar --&gt;\n",
       "                    &lt;div class=&quot;m2g-search-wrap&quot;&gt;\n",
       "                        &lt;input\n",
       "                            type=&quot;text&quot;\n",
       "                            class=&quot;m2g-searchbar form-control&quot;\n",
       "                            placeholder=&quot;Search&quot;\n",
       "                            aria-label=&quot;Search&quot;\n",
       "                            aria-describedby=&quot;basic-addon1&quot;\n",
       "                        /&gt;\n",
       "                        &lt;div class=&quot;m2g-search-options&quot;&gt;\n",
       "                            &lt;div class=&quot;m2g-option m2g-search-text sel&quot;&gt;Text&lt;/div&gt;\n",
       "                            &lt;div class=&quot;m2g-option m2g-search-smarts&quot;&gt;SMARTS&lt;/div&gt;\n",
       "                        &lt;/div&gt;\n",
       "                    &lt;/div&gt;\n",
       "\n",
       "                    &lt;!-- Action dropdown --&gt;\n",
       "                    &lt;div class=&quot;m2g-dropdown m2g-actions&quot;&gt;\n",
       "                        &lt;select&gt;\n",
       "                            &lt;option hidden&gt;-&lt;/option&gt;\n",
       "                            &lt;option value=&quot;select-all&quot;&gt;Select all&lt;/option&gt;\n",
       "                            &lt;option value=&quot;select-matching&quot;&gt;Select matching&lt;/option&gt;\n",
       "                            &lt;option value=&quot;unselect-all&quot;&gt;Unselect all&lt;/option&gt;\n",
       "                            &lt;option value=&quot;invert&quot;&gt;Invert&lt;/option&gt;\n",
       "                            &lt;option value=&quot;copy&quot;&gt;Copy to clipboard&lt;/option&gt;\n",
       "                            &lt;option value=&quot;save-smiles&quot;&gt;Save SMILES&lt;/option&gt;\n",
       "                            &lt;option value=&quot;save-csv&quot;&gt;Save CSV&lt;/option&gt;\n",
       "                        &lt;/select&gt;\n",
       "                        &lt;div class=&quot;m2g-icon&quot;&gt;\n",
       "                            &lt;svg width=&quot;20&quot; height=&quot;20&quot; viewBox=&quot;0 0 20 20&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;\n",
       "                                &lt;path d=&quot;M11.5 4C11.5 4.82843 10.8284 5.5 10 5.5C9.17157 5.5 8.5 4.82843 8.5 4C8.5 3.17157 9.17157 2.5 10 2.5C10.8284 2.5 11.5 3.17157 11.5 4ZM11.5 10C11.5 10.8284 10.8284 11.5 10 11.5C9.17157 11.5 8.5 10.8284 8.5 10C8.5 9.17157 9.17157 8.5 10 8.5C10.8284 8.5 11.5 9.17157 11.5 10ZM10 17.5C10.8284 17.5 11.5 16.8284 11.5 16C11.5 15.1716 10.8284 14.5 10 14.5C9.17157 14.5 8.5 15.1716 8.5 16C8.5 16.8284 9.17157 17.5 10 17.5Z&quot;/&gt;\n",
       "                            &lt;/svg&gt;\n",
       "                        &lt;/div&gt;\n",
       "                    &lt;/div&gt;\n",
       "                &lt;/div&gt;\n",
       "            &lt;/div&gt;\n",
       "\n",
       "            &lt;!-- Grid --&gt;\n",
       "            \n",
       "            &lt;div class=&quot;m2g-list&quot;&gt;&lt;div class=&quot;m2g-cell&quot; data-mols2grid-id=&quot;0&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;m2g-cb-wrap&quot;&gt;&lt;input type=&quot;checkbox&quot; tabindex=&quot;-1&quot; class=&quot;position-relative float-left cached_checkbox&quot;&gt;&lt;div class=&quot;m2g-cb&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data-mols2grid-id-display&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;m2g-cell-actions&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data data-img copy-me&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data data-E_tautomer_relative(kcal/mol) copy-me&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data data-SMILES copy-me&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;\n",
       "        &lt;/div&gt;\n",
       "        &lt;script&gt;\n",
       "            // list.js\n",
       "var listObj = new List(&#x27;mols2grid&#x27;, {\n",
       "    listClass: &#x27;m2g-list&#x27;,\n",
       "    valueNames: [{data: [&#x27;mols2grid-id&#x27;]}, &#x27;data-SMILES&#x27;, &#x27;data-E_tautomer_relative(kcal/mol)&#x27;, &#x27;data-img&#x27;, &#x27;data-mols2grid-id&#x27;, &#x27;data-mols2grid-id-display&#x27;],\n",
       "    item: &#x27;&lt;div class=&quot;m2g-cell&quot; data-mols2grid-id=&quot;0&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;m2g-cb-wrap&quot;&gt;&lt;input type=&quot;checkbox&quot; tabindex=&quot;-1&quot; class=&quot;position-relative float-left cached_checkbox&quot;&gt;&lt;div class=&quot;m2g-cb&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data-mols2grid-id-display&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;m2g-cell-actions&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data data-img copy-me&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data data-E_tautomer_relative(kcal/mol) copy-me&quot;&gt;&lt;/div&gt;&lt;div class=&quot;data data-SMILES copy-me&quot; style=&quot;display: none;&quot;&gt;&lt;/div&gt;&lt;/div&gt;&#x27;,\n",
       "    page: 24,\n",
       "    pagination: {\n",
       "        paginationClass: &quot;m2g-pagination&quot;,\n",
       "        item: &#x27;&lt;li class=&quot;page-item&quot;&gt;&lt;a class=&quot;page page-link&quot; href=&quot;#&quot; onclick=&quot;event.preventDefault()&quot;&gt;&lt;/a&gt;&lt;/li&gt;&#x27;,\n",
       "        innerWindow: 1,\n",
       "        outerWindow: 1,\n",
       "    },\n",
       "});\n",
       "listObj.remove(&quot;mols2grid-id&quot;, &quot;0&quot;);\n",
       "listObj.add([{&quot;data-SMILES&quot;: &quot;Nc1nccc(-c2[nH]cnc2-c2ccc(F)cc2)n1&quot;, &quot;data-E_tautomer_relative(kcal/mol)&quot;: &quot;0.00&quot;, &quot;data-img&quot;: null, &quot;mols2grid-id&quot;: 0, &quot;data-mols2grid-id-display&quot;: 0}, {&quot;data-SMILES&quot;: &quot;Nc1nccc(-c2nc[nH]c2-c2ccc(F)cc2)n1&quot;, &quot;data-E_tautomer_relative(kcal/mol)&quot;: &quot;2.22&quot;, &quot;data-img&quot;: null, &quot;mols2grid-id&quot;: 1, &quot;data-mols2grid-id-display&quot;: 1}, {&quot;data-SMILES&quot;: &quot;NC1=NC=C/C(=C2/N=CN=C2c2ccc(F)cc2)N1&quot;, &quot;data-E_tautomer_relative(kcal/mol)&quot;: &quot;13.59&quot;, &quot;data-img&quot;: null, &quot;mols2grid-id&quot;: 2, &quot;data-mols2grid-id-display&quot;: 2}, {&quot;data-SMILES&quot;: &quot;N=c1nc(-c2[nH]cnc2-c2ccc(F)cc2)cc[nH]1&quot;, &quot;data-E_tautomer_relative(kcal/mol)&quot;: &quot;16.10&quot;, &quot;data-img&quot;: null, &quot;mols2grid-id&quot;: 3, &quot;data-mols2grid-id-display&quot;: 3}, {&quot;data-SMILES&quot;: &quot;N=c1nccc(-c2nc[nH]c2-c2ccc(F)cc2)[nH]1&quot;, &quot;data-E_tautomer_relative(kcal/mol)&quot;: &quot;16.34&quot;, &quot;data-img&quot;: null, &quot;mols2grid-id&quot;: 4, &quot;data-mols2grid-id-display&quot;: 4}]);\n",
       "\n",
       "\n",
       "// filter\n",
       "if (window.parent.mols2grid_lists === undefined) {\n",
       "    window.parent.mols2grid_lists = {};\n",
       "}\n",
       "window.parent.mols2grid_lists[&quot;default&quot;] = listObj;\n",
       "\n",
       "\n",
       "// selection\n",
       "class MolStorage extends Map {\n",
       "    multi_set(_id, _smiles) {\n",
       "        for (let i = 0; i &lt; _id.length; i++) {\n",
       "            this.set(_id[i], _smiles[i])\n",
       "        }\n",
       "    }\n",
       "    multi_del(_id) {\n",
       "        for (let i = 0; i &lt; _id.length; i++) {\n",
       "            this.delete(_id[i])\n",
       "        }\n",
       "    }\n",
       "    to_dict() {\n",
       "        var content = &#x27;{&#x27;\n",
       "        for (let [key, value] of this) {\n",
       "            content += key + &#x27;:&#x27; + JSON.stringify(value) + &#x27;,&#x27;\n",
       "        }\n",
       "        content = content.length &gt; 1 ? content.slice(0, -1) : content\n",
       "        content += &#x27;}&#x27;\n",
       "        return content\n",
       "    }\n",
       "    to_keys() {\n",
       "        var content = []\n",
       "        for (let [key] of this) {\n",
       "            content.push(key)\n",
       "        }\n",
       "        return content\n",
       "    }\n",
       "    download_smi(fileName, allItems) {\n",
       "        var content = &#x27;&#x27;\n",
       "\n",
       "        if (allItems) {\n",
       "            // Gather all smiles\n",
       "            for (var item of allItems) {\n",
       "                var smiles = item.values()[&#x27;data-SMILES&#x27;]\n",
       "                var id = item.values()[&#x27;mols2grid-id&#x27;]\n",
       "                content += smiles + &#x27; &#x27; + id + &#x27;\\n&#x27;\n",
       "            }\n",
       "        } else {\n",
       "            // Gather selected smiles\n",
       "            for (let [key, value] of this) {\n",
       "                content += value + &#x27; &#x27; + key + &#x27;\\n&#x27;\n",
       "            }\n",
       "        }\n",
       "\n",
       "        var a = document.createElement(&#x27;a&#x27;)\n",
       "        var file = new Blob([content], { type: &#x27;text/plain&#x27; })\n",
       "        a.href = URL.createObjectURL(file)\n",
       "        a.download = fileName\n",
       "        a.click()\n",
       "        a.remove()\n",
       "    }\n",
       "}\n",
       "var SELECTION = new MolStorage();\n",
       "\n",
       "\n",
       "\n",
       "// kernel\n",
       "function add_selection(grid_id, _id, smiles) {\n",
       "    SELECTION.multi_set(_id, smiles);\n",
       "    let model = window.parent[&quot;_MOLS2GRID_&quot; + grid_id];\n",
       "    if (model) {\n",
       "        model.set(&quot;selection&quot;, SELECTION.to_dict());\n",
       "        model.save_changes();\n",
       "    }\n",
       "}\n",
       "function del_selection(grid_id, _id) {\n",
       "    SELECTION.multi_del(_id);\n",
       "    let model = window.parent[&quot;_MOLS2GRID_&quot; + grid_id];\n",
       "    if (model) {\n",
       "        model.set(&quot;selection&quot;, SELECTION.to_dict());\n",
       "        model.save_changes();\n",
       "    }\n",
       "}\n",
       "if (window.parent.IPython !== undefined) {\n",
       "    // Jupyter notebook\n",
       "    var kernel_env = &quot;jupyter&quot;;\n",
       "} else if (window.parent.google !== undefined) {\n",
       "    // Google colab\n",
       "    var kernel_env = &quot;colab&quot;;\n",
       "} else {\n",
       "    var kernel_env = null;\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "// sort\n",
       "var sortField = &#x27;mols2grid-id&#x27;\n",
       "var sortOrder = &#x27;asc&#x27;\n",
       "\n",
       "// Sort dropdown\n",
       "$(&#x27;#mols2grid .m2g-sort select&#x27;).change(sort)\n",
       "\n",
       "// Sort order\n",
       "$(&#x27;#mols2grid .m2g-order&#x27;).click(flipSort)\n",
       "\n",
       "function sort(e) {\n",
       "    if (e) {\n",
       "        sortField = e.target.value\n",
       "        var selectedOption = e.target.options[e.target.selectedIndex]\n",
       "        var sortFieldDisplay = selectedOption.text\n",
       "    }\n",
       "\n",
       "    // Sort\n",
       "    if (sortField == &#x27;checkbox&#x27;) {\n",
       "        listObj.sort(&#x27;mols2grid-id&#x27;, {order: sortOrder, sortFunction: checkboxSort})\n",
       "    } else {\n",
       "        listObj.sort(sortField, {order: sortOrder, sortFunction: mols2gridSortFunction})\n",
       "    }\n",
       "\n",
       "    // Update UI.\n",
       "    $(this).parent().find(&#x27;.m2g-display&#x27;).text(sortFieldDisplay)\n",
       "}\n",
       "\n",
       "// prettier-ignore\n",
       "function flipSort() {\n",
       "    $(this).parent().removeClass(&#x27;m2d-arrow-&#x27; + sortOrder)\n",
       "    sortOrder = sortOrder === &#x27;desc&#x27; ? &#x27;asc&#x27; : &#x27;desc&#x27;\n",
       "    $(this).parent().addClass(&#x27;m2d-arrow-&#x27; + sortOrder)\n",
       "    sort()\n",
       "}\n",
       "\n",
       "function mols2gridSortFunction(itemA, itemB, options) {\n",
       "    var x = itemA.values()[options.valueName]\n",
       "    var y = itemB.values()[options.valueName]\n",
       "    if (typeof x === &#x27;number&#x27;) {\n",
       "        if (isFinite(x - y)) {\n",
       "            return x - y\n",
       "        } else {\n",
       "            return isFinite(x) ? -1 : 1\n",
       "        }\n",
       "    } else {\n",
       "        x = x ? x.toLowerCase() : x\n",
       "        y = y ? y.toLowerCase() : y\n",
       "        return x &lt; y ? -1 : x &gt; y ? 1 : 0\n",
       "    }\n",
       "}\n",
       "function checkboxSort(itemA, itemB, options) {\n",
       "    if (itemA.elm !== undefined) {\n",
       "        var checkedA = itemA.elm.querySelector(&#x27;input[type=checkbox]&#x27;).checked\n",
       "        if (itemB.elm !== undefined) {\n",
       "            var checkedB = itemB.elm.querySelector(&#x27;input[type=checkbox]&#x27;).checked\n",
       "            if (checkedA &amp;&amp; !checkedB) {\n",
       "                return -1\n",
       "            } else if (!checkedA &amp;&amp; checkedB) {\n",
       "                return 1\n",
       "            } else {\n",
       "                return 0\n",
       "            }\n",
       "        } else {\n",
       "            return -1\n",
       "        }\n",
       "    } else if (itemB.elm !== undefined) {\n",
       "        return 1\n",
       "    } else {\n",
       "        return 0\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "// grid interactions (select, click, tooltip, key events)\n",
       "// Check if selection UI is supported.\n",
       "var supportSelection = eval(&#x27;True&#x27;.toLowerCase());\n",
       "\n",
       "listObj.on(&quot;updated&quot;, initInteraction);\n",
       "\n",
       "// (Re)initialiuze all grid interaction every time the grid changes.\n",
       "function initInteraction(list) {\n",
       "    initCellClick()\n",
       "    initToolTip()\n",
       "    initKeyboard()\n",
       "    if (supportSelection) initCheckbox()\n",
       "\n",
       "\n",
       "    // Hide pagination if there is only one page.\n",
       "    if (listObj.matchingItems.length &lt;= listObj.page) {\n",
       "        $(&#x27;#mols2grid .m2g-pagination&#x27;).hide()\n",
       "    } else {\n",
       "        $(&#x27;#mols2grid .m2g-pagination&#x27;).show()\n",
       "    }\n",
       "\n",
       "    // Add a bunch of phantom cells.\n",
       "    // These are used as filler to make sure that\n",
       "    // no grid cells need to be resized when there&#x27;s\n",
       "    // not enough results to fill the row.\n",
       "    $(&#x27;#mols2grid .m2g-list&#x27;).append(&#x27;&lt;div class=&quot;m2g-cell m2g-phantom&quot;&gt;&lt;/div&gt;&#x27;.repeat(11));\n",
       "}\n",
       "\n",
       "// Cell click handler.\n",
       "function initCellClick() {\n",
       "    $(&#x27;#mols2grid .m2g-cell&#x27;).off(&#x27;click&#x27;).click(function(e) {\n",
       "        if ($(e.target).hasClass(&#x27;m2g-info&#x27;) || $(e.target).is(&#x27;:checkbox&#x27;)) {\n",
       "            // Info button / Checkbox --&gt; do nothing.\n",
       "        } else if ($(e.target).is(&#x27;div&#x27;) &amp;&amp; $(e.target).hasClass(&#x27;data&#x27;)) {\n",
       "            // Data string --&gt; copy text.\n",
       "            copyOnClick(e.target)\n",
       "        } else if ($(e.target).hasClass(&#x27;m2g-callback&#x27;)) {\n",
       "            // Callback button.\n",
       "            onCallbackButtonClick(e.target)\n",
       "        } else {\n",
       "            // Outside checkbox --&gt; toggle the checkbox.\n",
       "            if (supportSelection) {\n",
       "                var chkbox = $(this).find(&#x27;input:checkbox&#x27;)[0]\n",
       "                chkbox.checked = !chkbox.checked\n",
       "                $(chkbox).trigger(&#x27;change&#x27;)\n",
       "            }\n",
       "        }\n",
       "    })\n",
       "}\n",
       "\n",
       "// Store an element&#x27;s text content in the clipboard.\n",
       "function copyOnClick(target) {\n",
       "    var text = $(target).text()\n",
       "    navigator.clipboard.writeText(text)\n",
       "\n",
       "    // Blink the cell to indicate that the text was copied.\n",
       "    $(target).addClass(&#x27;m2g-copy-blink&#x27;)\n",
       "    setTimeout(function() {\n",
       "        $(target).removeClass(&#x27;m2g-copy-blink&#x27;)\n",
       "    }, 450)\n",
       "}\n",
       "\n",
       "// Keyboard actions.\n",
       "function initKeyboard() {\n",
       "    // Disable scroll when pressing UP/DOWN arrows\n",
       "    $(&#x27;#mols2grid .m2g-cell&#x27;).off(&#x27;keydown&#x27;).keydown(function(e) {\n",
       "        if (e.which == 38 || e.which == 40) {\n",
       "            e.preventDefault()\n",
       "        }\n",
       "    })\n",
       "\n",
       "    $(&#x27;#mols2grid .m2g-cell&#x27;).off(&#x27;keyup&#x27;).keyup(function(e) {\n",
       "        var chkbox = $(this).find(&#x27;input:checkbox&#x27;)[0]\n",
       "        if (e.which == 13) {\n",
       "            // ENTER: toggle\n",
       "            chkbox.checked = !chkbox.checked\n",
       "            $(chkbox).trigger(&#x27;change&#x27;)\n",
       "        } else if (e.which == 27 || e.which == 8) {\n",
       "            // ESC/BACKSPACE: unselect\n",
       "            chkbox.checked = false\n",
       "            $(chkbox).trigger(&#x27;change&#x27;)\n",
       "        } else if (e.which == 37) {\n",
       "            // LEFT\n",
       "            $(this).prev().focus()\n",
       "        } else if (e.which == 39) {\n",
       "            // RIGHT\n",
       "            $(this).next().focus()\n",
       "        } else if (e.which == 38 || e.which == 40) {\n",
       "            var containerWidth = $(this).parent().outerWidth()\n",
       "            var cellWidth = $(this).outerWidth() + parseInt($(this).css(&#x27;marginLeft&#x27;)) * 2\n",
       "            var columns = Math.round(containerWidth / cellWidth)\n",
       "            var index = $(this).index()\n",
       "            if (e.which == 38) {\n",
       "                // UP\n",
       "                var indexAbove = Math.max(index - columns, 0)\n",
       "                $(this).parent().children().eq(indexAbove).focus()\n",
       "            } else if (e.which == 40) {\n",
       "                // DOWN    \n",
       "                var total = $(this).parent().children().length\n",
       "                var indexBelow = Math.min(index + columns, total)\n",
       "                $(this).parent().children().eq(indexBelow).focus()\n",
       "            }\n",
       "        }\n",
       "    })\n",
       "}\n",
       "\n",
       "// Show tooltip when hovering the info icon.\n",
       "function initToolTip() {\n",
       "    $(&#x27;#mols2grid .m2g-info&#x27;).off(&#x27;mouseenter&#x27;).off(&#x27;mouseleave&#x27;).off(&#x27;click&#x27;).mouseenter(function() {\n",
       "        // Show on enter\n",
       "        $(this).closest(&#x27;.m2g-cell&#x27;).find(&#x27;.m2g-tooltip[data-toggle=&quot;popover&quot;]&#x27;).popover(&#x27;show&#x27;)\n",
       "        $(&#x27;body &gt; .popover&#x27;).click(function(e) {\n",
       "            if ($(e.target).hasClass(&#x27;copy-me&#x27;)) {\n",
       "                copyOnClick(e.target)\n",
       "            } else if ($(e.target).is(&#x27;button&#x27;)) {\n",
       "                \n",
       "            }\n",
       "        })\n",
       "    }).mouseleave(function() {\n",
       "        // Hide on leave, unless sticky.\n",
       "        if (!$(this).closest(&#x27;.m2g-cell&#x27;).hasClass(&#x27;m2g-keep-tooltip&#x27;)) {\n",
       "            $(this).closest(&#x27;.m2g-cell&#x27;).find(&#x27;.m2g-tooltip[data-toggle=&quot;popover&quot;]&#x27;).popover(&#x27;hide&#x27;)\n",
       "        }\n",
       "    }).click(function() {\n",
       "        // Toggle sticky on click.\n",
       "        $(this).closest(&#x27;.m2g-cell&#x27;).toggleClass(&#x27;m2g-keep-tooltip&#x27;)\n",
       "\n",
       "        // Hide tooltip when sticky was turned off.\n",
       "        if ($(this).closest(&#x27;.m2g-cell&#x27;).hasClass(&#x27;m2g-keep-tooltip&#x27;)) {\n",
       "            $(this).closest(&#x27;.m2g-cell&#x27;).find(&#x27;.m2g-tooltip[data-toggle=&quot;popover&quot;]&#x27;).popover(&#x27;show&#x27;)\n",
       "        } else if (!$(this).closest(&#x27;.m2g-cell&#x27;).hasClass(&#x27;m2g-keep-tooltip&#x27;)) {\n",
       "            $(this).closest(&#x27;.m2g-cell&#x27;).find(&#x27;.m2g-tooltip[data-toggle=&quot;popover&quot;]&#x27;).popover(&#x27;hide&#x27;)\n",
       "        }\n",
       "    })\n",
       "}\n",
       "\n",
       "// Update selection on checkbox click.\n",
       "function initCheckbox() {\n",
       "    $(&quot;input:checkbox&quot;).off(&#x27;change&#x27;).change(function() {\n",
       "        var _id = parseInt($(this).closest(&quot;.m2g-cell&quot;).attr(&quot;data-mols2grid-id&quot;));\n",
       "        if (this.checked) {\n",
       "            var _smiles = $($(this).closest(&quot;.m2g-cell&quot;).children(&quot;.data-SMILES&quot;)[0]).text();\n",
       "            add_selection(&quot;default&quot;, [_id], [_smiles]);\n",
       "        } else {\n",
       "            del_selection(&quot;default&quot;, [_id]);\n",
       "        }\n",
       "    });\n",
       "}\n",
       "\n",
       "// Callback button\n",
       "function onCallbackButtonClick(target) {\n",
       "    var data = {}\n",
       "    data[&quot;mols2grid-id&quot;] = parseInt($(target).closest(&quot;.m2g-cell&quot;)\n",
       "                                            .attr(&quot;data-mols2grid-id&quot;));\n",
       "    data[&quot;img&quot;] = $(target).parent().siblings(&quot;.data-img&quot;).eq(0).get(0).innerHTML;\n",
       "    $(target).parent().siblings(&quot;.data&quot;).not(&quot;.data-img&quot;).each(function() {\n",
       "        let name = this.className.split(&quot; &quot;)\n",
       "            .filter(cls =&gt; cls.startsWith(&quot;data-&quot;))[0]\n",
       "            .substring(5);\n",
       "        data[name] = this.innerHTML;\n",
       "    });\n",
       "\n",
       "    \n",
       "    // Call custom js callback.\n",
       "    None\n",
       "    \n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "/**\n",
       " * Actions\n",
       " */\n",
       "\n",
       "// Listen to action dropdown.\n",
       "$(&#x27;#mols2grid .m2g-actions select&#x27;).change(function(e) {\n",
       "    var val = e.target.value\n",
       "    switch(val) {\n",
       "        case &#x27;select-all&#x27;:\n",
       "            selectAll()\n",
       "            break\n",
       "        case &#x27;select-matching&#x27;:\n",
       "            selectMatching()\n",
       "            break\n",
       "        case &#x27;unselect-all&#x27;:\n",
       "            unselectAll()\n",
       "            break\n",
       "        case &#x27;invert&#x27;:\n",
       "            invertSelection()\n",
       "            break\n",
       "        case &#x27;copy&#x27;:\n",
       "            copy()\n",
       "            break\n",
       "        case &#x27;save-smiles&#x27;:\n",
       "            saveSmiles()\n",
       "            break\n",
       "        case &#x27;save-csv&#x27;:\n",
       "            saveCSV()\n",
       "            break\n",
       "    }\n",
       "    $(this).val(&#x27;&#x27;) // Reset dropdown\n",
       "})\n",
       "\n",
       "// Check all.\n",
       "function selectAll(e) {\n",
       "    var _id = [];\n",
       "    var _smiles = [];\n",
       "    listObj.items.forEach(function (item) {\n",
       "        if (item.elm) {\n",
       "            item.elm.getElementsByTagName(&quot;input&quot;)[0].checked = true;\n",
       "        } else {\n",
       "            item.show()\n",
       "            item.elm.getElementsByTagName(&quot;input&quot;)[0].checked = true;\n",
       "            item.hide()\n",
       "        }\n",
       "        _id.push(item.values()[&quot;mols2grid-id&quot;]);\n",
       "        _smiles.push(item.values()[&quot;data-SMILES&quot;]);\n",
       "    });\n",
       "    add_selection(&quot;default&quot;, _id, _smiles);\n",
       "};\n",
       "\n",
       "\n",
       "// Check matching.\n",
       "function selectMatching(e) {\n",
       "    var _id = [];\n",
       "    var _smiles = [];\n",
       "    listObj.matchingItems.forEach(function (item) {\n",
       "        if (item.elm) {\n",
       "            item.elm.getElementsByTagName(&quot;input&quot;)[0].checked = true;\n",
       "        } else {\n",
       "            item.show()\n",
       "            item.elm.getElementsByTagName(&quot;input&quot;)[0].checked = true;\n",
       "            item.hide()\n",
       "        }\n",
       "        _id.push(item.values()[&quot;mols2grid-id&quot;]);\n",
       "        _smiles.push(item.values()[&quot;data-SMILES&quot;]);\n",
       "    });\n",
       "    add_selection(&quot;default&quot;, _id, _smiles);\n",
       "};\n",
       "\n",
       "// Uncheck all.\n",
       "function unselectAll(e) {\n",
       "    var _id = [];\n",
       "    listObj.items.forEach(function (item) {\n",
       "        if (item.elm) {\n",
       "            item.elm.getElementsByTagName(&quot;input&quot;)[0].checked = false;\n",
       "        } else {\n",
       "            item.show()\n",
       "            item.elm.getElementsByTagName(&quot;input&quot;)[0].checked = false;\n",
       "            item.hide()\n",
       "        }\n",
       "        _id.push(item.values()[&quot;mols2grid-id&quot;]);\n",
       "    });\n",
       "    del_selection(&quot;default&quot;, _id);\n",
       "};\n",
       "\n",
       "// Invert selection.\n",
       "function invertSelection(e) {\n",
       "    var _id_add = [];\n",
       "    var _id_del = [];\n",
       "    var _smiles = [];\n",
       "    listObj.items.forEach(function (item) {\n",
       "        if (item.elm) {\n",
       "            var chkbox = item.elm.getElementsByTagName(&quot;input&quot;)[0]\n",
       "            chkbox.checked = !chkbox.checked;\n",
       "        } else {\n",
       "            item.show()\n",
       "            var chkbox = item.elm.getElementsByTagName(&quot;input&quot;)[0]\n",
       "            chkbox.checked = !chkbox.checked;\n",
       "            item.hide()\n",
       "        }\n",
       "        if (chkbox.checked) {\n",
       "            _id_add.push(item.values()[&quot;mols2grid-id&quot;]);\n",
       "            _smiles.push(item.values()[&quot;data-SMILES&quot;]);\n",
       "        } else {\n",
       "            _id_del.push(item.values()[&quot;mols2grid-id&quot;]);\n",
       "        }\n",
       "    });\n",
       "    del_selection(&quot;default&quot;, _id_del);\n",
       "    add_selection(&quot;default&quot;, _id_add, _smiles);\n",
       "};\n",
       "\n",
       "// Copy to clipboard.\n",
       "function copy(e) {\n",
       "    // navigator.clipboard.writeText(SELECTION.to_dict());\n",
       "    content = _renderCSV(&#x27;\\t&#x27;)\n",
       "    navigator.clipboard.writeText(content)\n",
       "};\n",
       "\n",
       "// Export smiles.\n",
       "function saveSmiles(e) {\n",
       "    var fileName = &quot;selection.smi&quot;\n",
       "    if (SELECTION.size) {\n",
       "        // Download selected smiles\n",
       "        SELECTION.download_smi(fileName);\n",
       "    } else {\n",
       "        // Download all smiles\n",
       "        SELECTION.download_smi(fileName, listObj.items);\n",
       "    }\n",
       "};\n",
       "\n",
       "// Export CSV.\n",
       "function saveCSV(e) {\n",
       "    content = _renderCSV(&#x27;;&#x27;)\n",
       "    var a = document.createElement(&quot;a&quot;);\n",
       "    var file = new Blob([content], {type: &quot;text/csv&quot;});\n",
       "    a.href = URL.createObjectURL(file);\n",
       "    a.download = &quot;selection.csv&quot;;\n",
       "    a.click();\n",
       "    a.remove();\n",
       "};\n",
       "\n",
       "// Render CSV for export of clipboard.\n",
       "function _renderCSV(sep) {\n",
       "    // Same order as subset + tooltip\n",
       "    var columns = Array.from(listObj.items[0].elm.querySelectorAll(&quot;div.data&quot;))\n",
       "        .map(elm =&gt; elm.classList[1])\n",
       "        .filter(name =&gt; name !== &quot;data-img&quot;);\n",
       "    // Remove &#x27;data-&#x27; and img\n",
       "    var header = columns.map(name =&gt; name.slice(5));\n",
       "    // CSV content\n",
       "    header = [&quot;index&quot;].concat(header).join(sep);\n",
       "    var content = header + &quot;\\n&quot;;\n",
       "    listObj.items.forEach(function (item) {\n",
       "        let data = item.values();\n",
       "        let index = data[&quot;mols2grid-id&quot;];\n",
       "        if (SELECTION.has(index) || SELECTION.size === 0) {\n",
       "            content += index;\n",
       "            columns.forEach((key) =&gt; {\n",
       "                content += sep + data[key];\n",
       "            })\n",
       "            content += &quot;\\n&quot;;\n",
       "        }\n",
       "    });\n",
       "    return content\n",
       "}\n",
       "\n",
       "\n",
       "// generate images for the currently displayed molecules\n",
       "var draw_opts = {&quot;width&quot;: 200, &quot;height&quot;: 200};\n",
       "var json_draw_opts = JSON.stringify(draw_opts);\n",
       "\n",
       "var smarts_matches = {};\n",
       "\n",
       "// Load RDKit\n",
       "window\n",
       ".initRDKitModule()\n",
       ".then(function(RDKit) {\n",
       "    console.log(&#x27;RDKit version: &#x27;, RDKit.version());\n",
       "    window.RDKit = RDKit;\n",
       "    window.RDKitModule = RDKit;\n",
       "\n",
       "    // Searchbar\n",
       "    function SmartsSearch(query, columns) {\n",
       "    var smiles_col = columns[0];\n",
       "    smarts_matches = {};\n",
       "    var query = $(&#x27;#mols2grid .m2g-searchbar&#x27;).val();\n",
       "    var qmol = RDKit.get_qmol(query);\n",
       "    if (qmol.is_valid()) {\n",
       "        listObj.items.forEach(function (item) {\n",
       "            var smiles = item.values()[smiles_col]\n",
       "            var mol = RDKit.get_mol(smiles, &#x27;{&quot;removeHs&quot;: false }&#x27;);\n",
       "            if (mol.is_valid()) {\n",
       "                var results = mol.get_substruct_matches(qmol);\n",
       "                if (results === &quot;\\{\\}&quot;) {\n",
       "                    item.found = false;\n",
       "                } else {\n",
       "                    item.found = true;\n",
       "                    \n",
       "                    results = JSON.parse(results);\n",
       "                    \n",
       "                    var highlights = {&quot;atoms&quot;: [], &quot;bonds&quot;: []};\n",
       "                    results.forEach(function (match) {\n",
       "                        highlights[&quot;atoms&quot;].push(...match.atoms)\n",
       "                        highlights[&quot;bonds&quot;].push(...match.bonds)\n",
       "                    });\n",
       "                    \n",
       "                    var index = item.values()[&quot;mols2grid-id&quot;];\n",
       "                    smarts_matches[index] = highlights;\n",
       "                    \n",
       "                }\n",
       "            } else {\n",
       "                item.found = false;\n",
       "            }\n",
       "            mol.delete();\n",
       "        });\n",
       "    }\n",
       "    qmol.delete();\n",
       "}\n",
       "var search_type = &quot;Text&quot;;\n",
       "// Temporary fix for regex characters being escaped by list.js\n",
       "// This extends String.replace to ignore the regex pattern used by list.js and returns\n",
       "// the string unmodified. Other calls should not be affected, unless they use the exact\n",
       "// same pattern and replacement value.\n",
       "// TODO: remove once the issue is fixed in list.js and released\n",
       "String.prototype.replace = (function(_super) {\n",
       "    return function() {\n",
       "        if (\n",
       "            (arguments[0].toString() === &#x27;/[-[\\\\]{}()*+?.,\\\\\\\\^$|#]/g&#x27;)\n",
       "            &amp;&amp; (arguments[1] === &#x27;\\\\$&amp;&#x27;)\n",
       "        ) {\n",
       "            if (this.length === 0) {\n",
       "                return &#x27;&#x27;\n",
       "            }\n",
       "            return this\n",
       "        }\n",
       "        return _super.apply(this, arguments);\n",
       "    };         \n",
       "})(String.prototype.replace);\n",
       "\n",
       "// Switch search type (Text or SMARTS)\n",
       "$(&#x27;#mols2grid .m2g-search-options .m2g-option&#x27;).click(function() {\n",
       "    search_type = $(this).text();\n",
       "    $(&#x27;#mols2grid .m2g-search-options .m2g-option.sel&#x27;).removeClass(&quot;sel&quot;);\n",
       "    $(this).addClass(&quot;sel&quot;);\n",
       "});\n",
       "\n",
       "// Searchbar update event handler\n",
       "$(&#x27;#mols2grid .m2g-searchbar&#x27;).on(&quot;keyup&quot;, function(e) {\n",
       "    var query = e.target.value;\n",
       "    if (search_type === &quot;Text&quot;) {\n",
       "        smarts_matches = {};\n",
       "        listObj.search(query, [&#x27;data-mols2grid-id&#x27;, &#x27;data-E_tautomer_relative(kcal/mol)&#x27;]);\n",
       "    } else {\n",
       "        listObj.search(query, [&quot;data-SMILES&quot;], SmartsSearch);\n",
       "    }\n",
       "});\n",
       "\n",
       "    \n",
       "    // Generate images for the currently displayed molecules.\n",
       "RDKit.prefer_coordgen(true);\n",
       "function draw_mol(smiles, index, template_mol) {\n",
       "    var mol = RDKit.get_mol(smiles, &#x27;{&quot;removeHs&quot;: false }&#x27;);\n",
       "    var svg = &quot;&quot;;\n",
       "    if (mol.is_valid()) {\n",
       "        var highlights = smarts_matches[index];\n",
       "        if (highlights) {\n",
       "            var details = Object.assign({}, draw_opts, highlights);\n",
       "            details = JSON.stringify(details);\n",
       "            mol.generate_aligned_coords(template_mol, true);\n",
       "        } else {\n",
       "            var details = json_draw_opts;\n",
       "        }\n",
       "        svg = mol.get_svg_with_highlights(details);\n",
       "    }\n",
       "    mol.delete();\n",
       "    if (svg == &quot;&quot;) {\n",
       "        return &#x27;&lt;svg width=&quot;200&quot; height=&quot;200&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 200 200&quot;&gt;&lt;/svg&gt;&#x27;;\n",
       "    }\n",
       "    return svg;\n",
       "}\n",
       "\n",
       "// Update images when the list is updated.\n",
       "listObj.on(&quot;updated&quot;, function (list) {\n",
       "    var query = $(&#x27;#mols2grid .m2g-searchbar&#x27;).val();\n",
       "    var template_mol;\n",
       "    if (query === &quot;&quot;) {\n",
       "        smarts_matches = {};\n",
       "        template_mol = null;\n",
       "    } else {\n",
       "        template_mol = RDKit.get_qmol(query);\n",
       "        template_mol.set_new_coords(true);\n",
       "    }\n",
       "    $(&#x27;#mols2grid .m2g-cell&#x27;).each(function() {\n",
       "        var $t = $(this);\n",
       "        var smiles = $t.children(&quot;.data-SMILES&quot;).first().text();\n",
       "        var index = parseInt(this.getAttribute(&quot;data-mols2grid-id&quot;));\n",
       "        var svg = draw_mol(smiles, index, template_mol);\n",
       "        $t.children(&quot;.data-img&quot;).html(svg);\n",
       "    });\n",
       "    if (template_mol) {\n",
       "        template_mol.delete();\n",
       "    }\n",
       "});\n",
       "    \n",
       "\n",
       "    // Trigger update to activate tooltips, draw images, setup callbacks...\n",
       "    listObj.update();\n",
       "    \n",
       "    // Set iframe height to fit content.\n",
       "    fitIframe(window.frameElement);\n",
       "});\n",
       "        &lt;/script&gt;\n",
       "\n",
       "\n",
       "\n",
       "\n",
       "    &lt;/body&gt;\n",
       "&lt;/html&gt;\n",
       "\">\n",
       "</iframe>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mols2grid.display(df,mol_col=\"ROMol\",size=(200,200),subset=[\"img\",\"E_tautomer_relative(kcal/mol)\"],\n",
    "                  transform={\"E_tautomer_relative(kcal/mol)\": lambda x: \"%.2f\" % float(x)})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5UfJv86RCnju"
   },
   "source": [
    "### Acknowledgements\n",
    "\n",
    "I'd like to thank Zhen (Jack) Liu for his help with this notebook and contributions to Auto3D. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "authorship_tag": "ABX9TyPsLSWweOyNpRdMNpUd/0ol",
   "gpuType": "T4",
   "include_colab_link": true,
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "8d88dc5616db487ca1d0f224f10e9d7a": {
     "model_module": "mols2grid",
     "model_module_version": "^2.0.0",
     "model_name": "MolGridModel",
     "state": {
      "_dom_classes": [],
      "_model_module": "mols2grid",
      "_model_module_version": "^2.0.0",
      "_model_name": "MolGridModel",
      "_view_count": null,
      "_view_module": "mols2grid",
      "_view_module_version": "^2.0.0",
      "_view_name": "MolGridView",
      "callback_kwargs": "{}",
      "filter_mask": [],
      "grid_id": "default",
      "layout": "IPY_MODEL_bdb4e61bfe3b403f9a1d8347bb283603",
      "selection": "{}"
     }
    },
    "bdb4e61bfe3b403f9a1d8347bb283603": {
     "model_module": "@jupyter-widgets/base",
     "model_module_version": "1.2.0",
     "model_name": "LayoutModel",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
